package com.wuyan.web.base.helper.req;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.*;
import com.querydsl.jpa.impl.JPAUpdateClause;
import com.wuyan.web.base.entity.PubAccount;
import com.wuyan.web.base.entity.PubConfig;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * 自定义查询工具类-解析器
 *
 * @author wuyan
 */

@Slf4j
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CustomQueryHelper<T> {
    /**
     * 默认的时间日期格式
     */
    private static final String DEFAULT_DATE_FMT = "yyyy-MM-dd HH:mm:ss";

    /**
     * 被查询的数据实体类类型
     */
    private Class<T> entityClass;

    /**
     * 查询条件组
     */
    private List<CustomQueryParams> paramsList;

    /**
     * 查询排序组
     */
    private List<CustomQueryOrderParams> paramsOrders;

    /**
     * 查询体
     */
    private PathBuilder<T> pathBuilder;

    /**
     * 查询条件结果，被使用
     */
    private List<Predicate> predicates;

    /**
     * 查询排序结果，被使用
     */
    private List<OrderSpecifier<?>> orders;

    /**
     * 构建
     *
     * @return CustomQueryHelper<T>
     */
    public CustomQueryHelper<T> structure() {
        predicates = new ArrayList<>();

        if (null == entityClass) {
            return this;
        }

        if (this.paramsList != null && !this.paramsList.isEmpty()) {
            paramsList.forEach(t -> {
                Predicate predicate = analysisWhere(t);
                if (predicate != null) {
                    predicates.add(predicate);
                }
            });
        }

        if (this.paramsOrders != null && !this.paramsOrders.isEmpty()) {
            orders = new ArrayList<>();
            paramsOrders.forEach(t -> {
                OrderSpecifier<?> order = analysisOrder(t);
                if (order != null) {
                    orders.add(order);
                }
            });
        }

        return this;
    }

    /**
     * order排序
     *
     * @param t 内容
     * @return OrderSpecifier<?>
     */
    private OrderSpecifier<?> analysisOrder(CustomQueryOrderParams t) {
        if (!t.check()) {
            return null;
        }
        t.init();

        String filedTypeName;
        try {
            filedTypeName = getFiledTypeName(t.getFiledName());
        } catch (NoSuchFieldException e) {
            log.error("查询列：" + t.getFiledName() + "不存在，已自动跳过。" + e.getMessage());
            return null;
        }

        switch (filedTypeName) {
            case "Integer":
                return new OrderSpecifier<>(t.getOrder(), pathBuilder.getNumber(t.getFiledName(), Integer.class));
            case "Long":
                return new OrderSpecifier<>(t.getOrder(), pathBuilder.getNumber(t.getFiledName(), Long.class));
            case "String":
                return new OrderSpecifier<>(t.getOrder(), pathBuilder.getString(t.getFiledName()));
            case "Boolean":
                return new OrderSpecifier<>(t.getOrder(), pathBuilder.getBoolean(t.getFiledName()));
            case "LocalDateTime":
                return new OrderSpecifier<>(t.getOrder(), pathBuilder.getDateTime(t.getFiledName(), LocalDateTime.class));
            default:
                return null;
        }
    }

    /**
     * 设置更新数据，单个
     *
     * @param jpaUpdateClause 更新主体
     * @param field           字段名
     * @param value           值
     * @return JPAUpdateClause
     */
    public JPAUpdateClause setJPAUpdateClause(JPAUpdateClause jpaUpdateClause, String field, String value) {
        String filedTypeName;
        try {
            filedTypeName = getFiledTypeName(field);
        } catch (NoSuchFieldException e) {
            log.error("查询列：" + field + "不存在，已自动跳过。" + e.getMessage());
            return null;
        }

        switch (filedTypeName) {
            case "Integer":
                Path<Integer> pathInteger = pathBuilder.getNumber(field, Integer.class);
                return jpaUpdateClause.set(pathInteger, Integer.parseInt(value));
            case "Long":
                Path<Long> pathLong = pathBuilder.getNumber(field, Long.class);
                return jpaUpdateClause.set(pathLong, Long.parseLong(value));
            case "String":
                StringPath pathString = pathBuilder.getString(field);
                return jpaUpdateClause.set(pathString, value);
            case "Boolean":
                BooleanPath booleanPath = pathBuilder.getBoolean(field);
                return jpaUpdateClause.set(booleanPath, Boolean.parseBoolean(value));
            case "LocalDateTime":
                DateTimePath<LocalDateTime> dateTimePath = pathBuilder.getDateTime(field, LocalDateTime.class);
                LocalDateTime dateTime = LocalDateTime.parse(value, DateTimeFormatter.ofPattern(DEFAULT_DATE_FMT));
                return jpaUpdateClause.set(dateTimePath, dateTime);
            default:
                return null;
        }
    }


    /**
     * where解析
     *
     * @param t 数据体
     * @return Predicate
     */
    private Predicate analysisWhere(CustomQueryParams t) {
        if (!t.check()) {
            return null;
        }

        switch (t.getOp()) {
            // 等于
            case "eq":
                return pathBuilder.get(t.getLeft()).eq(t.getRight()[0]);
            // 不等于
            case "ne":
                return pathBuilder.get(t.getLeft()).ne(t.getRight()[0]);
            // 包含
            case "in":
                return pathBuilder.get(t.getLeft()).in(t.getRight());
            // 不包含
            case "notIn":
                return pathBuilder.get(t.getLeft()).notIn(t.getRight());
            case "isNull":
                return pathBuilder.get(t.getLeft()).isNull();
            case "isNotNull":
                return pathBuilder.get(t.getLeft()).isNotNull();
            default:
                break;
        }

        String filedTypeName;
        try {
            filedTypeName = getFiledTypeName(t.getLeft());
        } catch (NoSuchFieldException e) {
            log.error("查询列：" + t.getLeft() + "不存在，已自动跳过。" + e.getMessage());
            return null;
        }

        switch (filedTypeName) {
            case "String":
                StringPath string = pathBuilder.getString(t.getLeft());
                return analysisOperateForString(string, t);
            case "Integer":
                NumberPath<Integer> integer = pathBuilder.getNumber(t.getLeft(), Integer.class);
                return analysisOperateForNumber(integer, t, Integer.class);
            case "Long":
                NumberPath<Long> longInt = pathBuilder.getNumber(t.getLeft(), Long.class);
                return analysisOperateForNumber(longInt, t, Long.class);
            case "LocalDateTime":
                DateTimePath<LocalDateTime> dateTime = pathBuilder.getDateTime(t.getLeft(), LocalDateTime.class);
                return analysisOperateForDateTime(dateTime, t);
            default:
                return null;
        }
    }

    /**
     * 解析字符串
     *
     * @param path 节点
     * @param t    查询条件
     * @return Predicate
     */
    private Predicate analysisOperateForString(StringPath path, CustomQueryParams t) {
        String right0Str = t.getRight()[0].toString();

        switch (t.getOp()) {
            case "like":
                return path.like("%" + right0Str + "%");
            case "leftLike":
                return path.like("%" + right0Str);
            case "rightLike":
                return path.like(right0Str + "%");
            default:
                return null;
        }
    }

    /**
     * 解析时间
     *
     * @param path 节点
     * @param t    查询条件
     * @return Predicate
     */
    private Predicate analysisOperateForDateTime(DateTimePath<LocalDateTime> path, CustomQueryParams t) {
        String right0Str = t.getRight()[0].toString();
        LocalDateTime right0 = LocalDateTime.parse(right0Str, DateTimeFormatter.ofPattern(DEFAULT_DATE_FMT));

        switch (t.getOp()) {
            case "gt":
                return path.gt(right0);
            case "goe":
                return path.goe(right0);
            case "lt":
                return path.lt(right0);
            case "loe":
                return path.loe(right0);
            case "between":
                String right1Str = t.getRight()[1].toString();
                LocalDateTime right1 = LocalDateTime.parse(right1Str, DateTimeFormatter.ofPattern(DEFAULT_DATE_FMT));
                return path.between(right0, right1);
            default:
                return null;
        }
    }

    /**
     * 解析数字类型
     *
     * @param path   节点
     * @param t      查询条件
     * @param kClass 节点对应的数据类型
     * @param <K>    泛型
     * @return Predicate
     */
    private <K extends Number & Comparable<?>> Predicate analysisOperateForNumber
    (NumberPath<K> path, CustomQueryParams t, Class<K> kClass) {
        K right = kClass.cast(t.getRight()[0]);

        switch (t.getOp()) {
            case "gt":
                return path.gt(right);
            case "goe":
                return path.goe(right);
            case "lt":
                return path.lt(right);
            case "loe":
                return path.loe(right);
            case "between":
                K right1 = kClass.cast(t.getRight()[1]);
                return path.between(right, right1);
            default:
                return null;
        }
    }

    /**
     * 根据属性名获取属性类型名称（简单名称）
     *
     * @param filed 属性名称
     * @return String
     * @throws NoSuchFieldException 没有该字段异常
     */
    private String getFiledTypeName(String filed) throws NoSuchFieldException {
        Field field = entityClass.getDeclaredField(filed);
        return field.getType().getSimpleName();
    }

    /**
     * 初始化
     *
     * @param entityClass 实体类类类型
     * @param params      参数
     * @param orders      排序
     * @param <T>         类型
     * @return CustomQueryHelper<T>
     * @throws JsonProcessingException   JSON格式化错误
     * @throws ClassNotFoundException    类不存在
     * @throws NoSuchMethodException     getMetadata方法不存在
     * @throws IllegalAccessException    e
     * @throws InvocationTargetException e
     * @throws InstantiationException    e
     */
    public static <T> CustomQueryHelper<T> init(Class<T> entityClass,
                                                String params,
                                                String orders
    ) throws JsonProcessingException, ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        ObjectMapper mapper = new ObjectMapper();
        // 查询参数
        List<CustomQueryParams> paramsList = mapper.readValue(params, new TypeReference<List<CustomQueryParams>>() {
        });
        // 排序参数
        List<CustomQueryOrderParams> orderList = mapper.readValue(orders, new TypeReference<List<CustomQueryOrderParams>>() {
        });

        return init(entityClass, paramsList, orderList);
    }

    /**
     * 初始化
     *
     * @param entityClass 实体类类类型
     * @param paramsList  参数
     * @param orderList   排序
     * @param <T>         类型
     * @return CustomQueryHelper<T>
     * @throws ClassNotFoundException    类不存在
     * @throws NoSuchMethodException     getMetadata方法不存在
     * @throws IllegalAccessException    e
     * @throws InvocationTargetException e
     * @throws InstantiationException    e
     */
    public static <T> CustomQueryHelper<T> init(Class<T> entityClass,
                                                List<CustomQueryParams> paramsList,
                                                List<CustomQueryOrderParams> orderList
    ) throws ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {

        String qName = entityClass.getPackage().getName() + ".Q" + entityClass.getSimpleName();
        Class<?> qClass = Class.forName(qName);

        // 获取带参数的构造器
        Constructor<?> con = qClass.getConstructor(String.class);
        // 通过带参构造方法对象，创建对象
        char[] chars = entityClass.getSimpleName().toCharArray();
        chars[0] += 32;
        Object obj = con.newInstance(String.valueOf(chars));

        Method getMetadata = qClass.getMethod("getMetadata");
        PathMetadata pathMetadata = (PathMetadata) getMetadata.invoke(obj);

        PathBuilder<T> pathBuilder = new PathBuilder<>(entityClass, pathMetadata);

        return CustomQueryHelper.<T>builder()
                .paramsList(paramsList)
                .pathBuilder(pathBuilder)
                .paramsOrders(orderList)
                .entityClass(entityClass)
                .build()
                .structure();
    }
}
